% A module for reporting the results of object matching.  This probably won't be used in the
% production tool, but it should be useful for interactive debugging of the Prolog
% infrastructure, and perhaps more importantly, it serves to define what an "answer" is for
% when it comes time to import the results back into OOAnalyzer.

:- use_module(library(ansi_term), [ansi_format/3]).
:- use_module(library(aggregate), [aggregate_all/3]).

:- ensure_loaded(setup).

psolve_no_halt(X) :-
    ws_init,
    reportStage('Loading data'),
    loadInitialFacts(X),
    (loginfo('Guessing is '), guessingDisabled -> loginfoln('disabled.'); loginfoln('enabled.')),
    (loginfo('Profiling is '), profilingEnabled -> loginfoln('enabled.'); loginfoln('disabled.')),
    (loginfo('RTTI is '), rTTIEnabled -> loginfoln('enabled.'); loginfoln('disabled.')),
    (profilingEnabled ->
         setup_call_cleanup(
             alarm(60, (show_progress, uninstall_alarm(Id), install_alarm(Id, 60)), Id),
             solve(ooscript),
             remove_alarm(Id));
     solve(ooscript)),
    reportResults,
    loginfoln('Object oriented analysis and reporting complete, exiting.'),
    !.

psolve(X) :- psolve_no_halt(X), halt.

% Cory's no longer sure what we were catching.  But if we need to catch something again, we
% should do it here by wrapping psolve(X) and psolve_no_halt() with some code like:
%
% catch((something), E, somehow_report_error)

% This definition of progress is for when we're NOT running from within OOAnalyzer, which is
% probably the same circumstances where we want this reporting module.
progress(N) :-
  loginfoln('There are ~D known facts.', [N]).

% Streams and colors are here because they don't affect the OOAnalyzer C++ mode.
logStream('FATAL', user_output).
logStream('ERROR', user_output).
logStream('WARN', user_output).
logStream('INFO', user_output).
logStream('DEBUG', user_output).
logStream('TRACE', user_output).
logStream('CRAZY', user_output).

logColor('FATAL', [bold,fg(red)]).
logColor('ERROR', [fg(red)]).
logColor('WARN', [fg(yellow)]).
logColor('INFO', [fg(green)]).
logColor('DEBUG', [fg(magenta)]).
logColor('TRACE', [fg(blue)]).
logColor('CRAZY', [fg(cyan)]).

% In the OOAnalyzer binary, these are passed to a proper Pharos logging stream.
log(Importance, Message) :-
    (numericLogLevel(Importance, MsgNumber),
     logLevel(LogNumber),
     MsgNumber =< LogNumber,
     logStream(Importance, Stream)
    ) -> with_output_to(Stream,
                        (logColor(Importance, Color),
                         ansi_format(Color, '~a', [Message])))
    ; true.

logln(Importance, Message) :-
    atomic_concat(Message, '\n', MessageNL),
    log(Importance, MessageNL).

% ============================================================================================
% The main reporting rule.
% ============================================================================================

writePredicate(P) :-
    writeHex(P, [quoted(true), spacing(next_argument), nl(true), fullstop(true)]).

% A strange hybrid case.  We store these as find(C1, C2), but we frequently refer to them in
% our code as factMergeClasses/2, which seems to be the more natural way to express them.  This
% little bit of syntactic sugar should help ease that gap.
reportPredicate(factMergeClasses/2) :-
    (setof(factMergeClasses(C1, C2), find(C1, C2), Facts),
     forall(member(Pred, Facts), writePredicate(Pred)), !
    ); true.

reportPredicate(Name/Arity) :-
    functor(Head, Name, Arity),
    (setof(Head, Head, Functors),
     forall(member(Pred, Functors), writePredicate(Pred)), !
    ); true.

reportResults :-
    reportStage('reportResults'),
    writeln('% Prolog results autogenerated by OOAnalyzer.'),
    reportPredicate(finalFileInfo/2),
    reportPredicate(finalVFTable/5),
    reportPredicate(finalVFTableEntry/3),
    reportPredicate(finalVBTable/4),
    reportPredicate(finalVBTableEntry/3),
    reportPredicate(finalClass/6),
    reportPredicate(finalResolvedVirtualCall/3),
    reportPredicate(finalEmbeddedObject/4),
    reportPredicate(finalInheritance/5),
    reportPredicate(finalMember/4),
    reportPredicate(finalMemberAccess/4),
    reportPredicate(finalMethodProperty/3),
    reportPredicate(finalThunk/2),
    reportPredicate(finalDemangledName/4),
    writeln('% Object detection reporting complete.'),
    % Moved guessing statictics to the very end, because some debugging messages about
    % discarding worthless classes end up obscuring the "summary" otherwise.
    reportGuessedStatistics.

reportMethodFacts :-
    reportPredicate(factMethod/1),
    reportPredicate(factConstructor/1),
    reportPredicate(factRealDestructor/1),
    reportPredicate(factDeletingDestructor/1),
    reportPredicate(factVirtualFunctionCall/5).

reportNOTMethodFacts :-
    reportPredicate(factNOTMethod/1),
    reportPredicate(factNOTConstructor/1),
    reportPredicate(factNOTRealDestructor/1),
    reportPredicate(factNOTDeletingDestructor/1),
    reportPredicate(factNOTVirtualFunctionCall/5).

reportVirtualTableFacts :-
    reportPredicate(factVFTable/1),
    reportPredicate(factVFTableWrite/4),
    reportPredicate(factVFTableOverwrite/4),
    reportPredicate(factVFTableEntry/3),
    reportPredicate(factVFTableSizeGTE/2),
    reportPredicate(factVFTableSizeLTE/2),
    reportPredicate(factVBTable/1),
    reportPredicate(factVBTableWrite/4),
    reportPredicate(factVBTableEntry/3).

reportNOTVirtualTableFacts :-
    reportPredicate(factNOTVFTable/1),
    reportPredicate(factNOTVFTableEntry/3),
    reportPredicate(factNOTVBTable/1),
    reportPredicate(factNOTVBTableEntry/3).

reportClassFacts :-
    reportPredicate(factObjectInObject/3),
    reportPredicate(factDerivedClass/3),
    reportPredicate(factEmbeddedObject/3),
    reportPredicate(factClassHasNoBase/1),
    reportPredicate(factClassHasUnknownBase/1),
    reportPredicate(factClassCallsMethod/2),
    reportPredicate(factClassSizeGTE/2),
    reportPredicate(factClassSizeLTE/2).

reportFacts :-
    reportMethodFacts,
    reportNOTMethodFacts,
    reportVirtualTableFacts,
    reportNOTVirtualTableFacts,
    reportClassFacts,
    reportPredicate(factMergeClasses/2),
    reportPredicate(factNOTMergeClasses/2).

% ============================================================================================
% Rules for counting guesses at the end of execution.
% ============================================================================================

% Print how many conclusions were guessed versus how many were reasoned.  We could also report
% the actual specific guesssed facts if we wanted.
reportCounts(Name, P1/A1, P2/A2, P3/A3, P4/A4) :-
    functor(F1, P1, A1),
    functor(F2, P2, A2),
    functor(F3, P3, A3),
    functor(F4, P4, A4),
    aggregate_all(count, F1, C1),
    aggregate_all(count, F2, C2),
    aggregate_all(count, F3, C3),
    aggregate_all(count, F4, C4),
    loginfoln('Guessed ~D ~a of ~D, guessed contrary conclusions: ~D of ~D', [C1, Name, C2, C3, C4]).

reportGuessedStatistics :-
    reportCounts('methods', guessedMethod/1, factMethod/1,
                 guessedNOTMethod/1, factNOTMethod/1),
    reportCounts('constructors', guessedConstructor/1, factConstructor/1,
                 guessedNOTConstructor/1, factNOTConstructor/1),
    reportCounts('destructors', guessedRealDestructor/1, factRealDestructor/1,
                 guessedNOTRealDestructor/1, factNOTRealDestructor/1),
    reportCounts('deleting destructors', guessedDeletingDestructor/1, factDeletingDestructor/1,
                 guessedNOTDeletingDestructor/1, factNOTDeletingDestructor/1),
    reportCounts('virtual function calls', guessedVirtualFunctionCall/5,
                 factVirtualFunctionCall/5, guessedNOTVirtualFunctionCall/5,
                 factNOTVirtualFunctionCall/5),
    reportCounts('virtual function tables', guessedVFTable/1, factVFTable/1,
                 guessedNOTVFTable/1, factNOTVFTable/1),
    reportCounts('virtual base tables', guessedVBTable/1, factVBTable/1,
                 guessedNOTVBTable/1, factNOTVBTable/1),
    reportCounts('virtual function table entries', guessedVFTableEntry/3, factVFTableEntry/3,
                 guessedNOTVFTableEntry/3, factNOTVFTableEntry/3),
    reportCounts('derived classes', guessedDerivedClass/3, factDerivedClass/3,
                 guessedNOTDerivedClass/3, factNOTDerivedClass/3),
    reportCounts('embedded objects', guessedEmbeddedObject/3, factEmbeddedObject/3,
                 guessedNOTEmbeddedObject/3, factNOTEmbeddedObject/3),
    reportCounts('has a base class', guessedClassHasUnknownBase/1, factClassHasUnknownBase/1,
                 guessedClassHasNoBase/1, factClassHasNoBase/1),
%%    reportCounts('class mergers', guessedMergeClasses/2, factMergeClasses/2,
%%                 guessedNOTMergeClasses/2, factNOTMergeClasses/2)
    true.


/* Local Variables:   */
/* mode: prolog       */
/* fill-column:    95 */
/* comment-column: 0  */
/* End:               */
